#coding=utf8

from network import LEN_HEADER,LEN_HEADER_SIZE,CMD_MARK,CMD_MARK_SIZE
import proto2packet
import cmd2proto
import gevent
import struct
from gevent.queue import Queue

import message
@message.observable
class PlayerTransport(object):
	DATA_ARRIVE = 'DATA'
	TRANSPORT_CLOSE = 'CLOSE'
	def __init__(self, sock):
		print 'create transport'
		self._sock = sock
		self._send_queue = Queue()
		self.buff = ''

		self._lets = [gevent.spawn(self._send_func),
				gevent.spawn(self._recv_func)]

		for let in self._lets:
			let.link(self._close)

	def _close(self, arg):
		for let in self._lets:
			try:
				let.kill()
			except Exception:
				pass
		self._sock.close()
		self.pub(self.TRANSPORT_CLOSE)

	def getpeername(self):
		return self._sock.getpeername()

	def getsockname(self):
		return self._sock.getsockname()

	def send(self, msg):
		self._send_queue.put(msg)
	
	def recv(self):
#		print 'call recv:', repr(self.buff)
		assert self.buff
		t, self.buff = self.buff, ''
		return t

	def _send_func(self):
		print 'Transport send start'
		while True:
			msg = self._send_queue.get()
			proto2packet.send(self._sock,msg)
			#print 'send msg'
		print 'Transport send break'
	
	def _recv_func(self):
		print 'Transport recv start'
		while True:
			self.buff = self._sock.recv(4096)
			if not self.buff:
				break
			self.pub(self.DATA_ARRIVE)
		print 'Transport send break'

		

class Protocol(object):
	class NoBuff(StandardError):
		pass
	def __init__(self):
		self.buf = ''
		self.need = 0
		self.cmd = 0
	def append_data(self,data):
		self.buf += data
	def process(self):
		while True:
			if self.need <= 0:
				try:
					self._read_need()
				except Protocol.NoBuff:
					raise
			if len(self.buf) < self.need:
				raise Protocol.NoBuff()
			packet,self.buf = self.buf[:self.need],self.buf[self.need:]
			self.need = 0
			return self.cmd,packet

	def _read_need(self):
		if len(self.buf) < LEN_HEADER_SIZE + CMD_MARK_SIZE:
			raise Protocol.NoBuff()
#		print 'buf len: %d'%len(self.buf)
		need = self.buf[:LEN_HEADER_SIZE]
		cmd = self.buf[LEN_HEADER_SIZE:LEN_HEADER_SIZE+CMD_MARK_SIZE]
		self.buf = self.buf[LEN_HEADER_SIZE+CMD_MARK_SIZE:]
		self.need, = struct.unpack(LEN_HEADER,need)
		self.cmd, = struct.unpack(CMD_MARK,cmd)

def show_msg(func):
	def ret_func(self,msg):
		print 'handle msg: %s'%msg.DESCRIPTOR.name
		func(self,msg)
	return ret_func


class TransportHandler(object):
	def __init__(self,sock):
		self._protocol = Protocol()
		self._transport = PlayerTransport(sock)
		self._transport.sub(PlayerTransport.DATA_ARRIVE,self._on_recv_data)
		self._transport.sub(PlayerTransport.TRANSPORT_CLOSE,self._on_close_transport)
		self._proto2handler = {}
		self.init_proto_handler()
	
	def init_proto_handler(self):
		raise NotImplementedError

	def _on_recv_data(self):
		self.append_data(self._transport.recv())
	
	def _on_close_transport(self):
		self._transport = None

	def _send(self,msg):
		self._transport.send(msg)

	def append_data(self,data):
		self._protocol.append_data(data)
		while True:
			try:
				cmd,packet = self._protocol.process()
			except Protocol.NoBuff:
				#print 'no enough data'
				break
			print 'cmdcode: %d'%cmd
			msg = cmd2proto.proto_dict[cmd]()	
			msg.ParseFromString(packet)
			self._proto2handler[cmd](msg)
